今天開始來處理上傳檔案到GoogleDrive的部分~
首先要先能在LineBot上拿到使用者傳送的檔案,我們在Callback
加上一個case,透過*linebot.FileMessage
來判斷傳送的類型是檔案,接著比較不一樣的是沒辦法直接透過message.XXX拿到檔案內容,而是要從message拿到ID並透過GetMessageContent()
把傳送的檔案內容取出來。
// internal\router\api\v1\callback.go
case *linebot.FileMessage:
lineID := event.Source.UserID
content, err := app.LineBotClient.GetMessageContent(message.ID).Do()
if err != nil {
log.Println(err)
return
}
app.DriveService.UploadFile(ctx, lineID, message.FileName, content.Content)
if err != nil {
log.Println(err)
return
}
if _, err = app.LineBotClient.ReplyMessage(event.ReplyToken, linebot.NewTextMessage("成功上傳: "+message.FileName)).Do(); err != nil {
log.Println(err)
}
接著我們來實現上面DriveService的UploadFile
,輸入中的message.FileName
是從Line拿到的檔案名稱,同時也是我們檔案上傳到Drive的名稱,content.Content
則是一個熟悉的io.ReadCloser
,我們可以操控他來進行檔案的操作。
這邊先大概講一下這個Service做了什麼,我們一樣先去拿Dynamodb的token然後New一個可以操作的GoogleDrive出來,接著把剛剛收到的content
丟到domain裡去儲存到本地端,接著上傳檔案到指定的資料夾內。
// internal\app\service\drive\drive_service.go
func (dr *GoogleDriveService) UploadFile(ctx context.Context, lineID string, fileName string, content io.ReadCloser) error {
dToken, err := dr.driveServiceDynamodb.GetGoogleOAuthToken(lineID)
if err != nil {
log.Println(err)
return err
}
tok := oauth2.Token{
AccessToken: dToken.AccessToken,
TokenType: dToken.TokenType,
RefreshToken: dToken.RefreshToken,
Expiry: dToken.Expiry,
}
d, err := dr.driveServiceGoogleOA.NewGoogleDrive(ctx, &tok)
if err != nil {
log.Println(err)
return err
}
file, err := domainDrive.SaveContent(content)
if err != nil {
log.Println(err)
return err
}
log.Println("START Upload File To Drive")
// 假設預設的儲存路徑
folderID := "1kpLZfvk9XmSr4xtDvczAqYHIF8P3r8bk"
err = d.UploadFile(folderID, fileName, file)
if err != nil {
log.Println("err:", err)
return err
}
return nil
}
那我們先來看domain的部分,首先domain下建立drive\drive.go
,還有在最外層目錄建立tmp資料夾。我們會透過os.CreateTemp
在./tmp
下建立暫存檔,透過io.Copy
把content複製進去,寫完檔案後要記得file.Seek(0, 0)
把檔案指到開頭,別人在使用我們回傳的*os.File
時才能順利從頭讀到檔案,最後就可以把content關了。
package drive
import (
"io"
"log"
"os"
)
// Save content to ./tmp
func SaveContent(content io.ReadCloser) (*os.File, error) {
defer content.Close()
file, err := os.CreateTemp("./tmp", "")
if err != nil {
return nil, err
}
_, err = io.Copy(file, content)
if err != nil {
return nil, err
}
_, err = file.Seek(0, 0)
if err != nil {
return nil, err
}
log.Printf("Saved %s", file.Name())
return file, nil
}
這邊可以注意到我只有defer content.Close()
,但是並沒有寫defer file.Close()
,因為我們希望直接透過這個*os.File
讓adapter直接將檔案上傳到google drive上。
到這邊我們先註解掉drive_service.go
,log.Println("START Upload File To Drive")
下面的那些code,然後建立一個測試的txt。
把測試.txt
丟到我們的Linebot上
可以看到tmp下面就成功建立了暫存檔,同時裡面的內容也有被正確的寫入,那我們今天就先寫到這,明天再繼續寫adapter的部分,明天見囉~